Skip to content
标签
数据库
字数
6598 字
阅读时间
29 分钟

一、概述

是数据访问层框架,底层是对 JDBC 的封装。使用 mybatis 时不需要编写实现类,只需要写需要执行的 sql 命令,同时解决了jdbc效率低,sql在程序中拼接、sql参数赋值、ResultSet遍历不利于维护的问题。

1.1 框架中主要的类

  • 运行时相关的类

    ResourcesMyBatis 中 IO 流的工具类加载配置文件
    SqlSessionFactoryBuilder() 构建器创建 SqlSessionFactory 接口的实现类
    XMLConfigBuilder MyBatis 全局配置文件内容构建器类作用负责读取流内容并转换为 JAVA 代码
    Configuration 封装了全局配置文件所有配置信息.全局配置文件内容存放在 Configuration 中
    DefaultSqlSessionFactorySqlSessionFactory接口的实现类
    Transaction 事务类SqlSession 会带有一个 Transaction 对象.
    TransactionFactory 事务工厂负责生产 Transaction
    Executor MyBatis 执行器负责执行 SQL 命令,默认的执行器 SimpleExcutor
    批量操作 BatchExcutor 通过 openSession(参数控制)
    DefaultSqlSessionSqlSession 接口的实现类
    ExceptionFactoryMyBatis异常工厂

1.2 执行流程描述

在 MyBatis 运行开始时需要先通过 Resources 加载全局配置文件.下面 需要实例化 SqlSessionFactoryBuilder 构建器.帮助 SqlSessionFactory 接 口实现类 DefaultSqlSessionFactory. 在实例化 DefaultSqlSessionFactory 之前需要先创建 XmlConfigBuilder 解析全局配置文件流,并把解析结果存放在 Configuration 中.之后把 Configuratin 传递给 DefaultSqlSessionFactory.到此 SqlSessionFactory 工 厂创建成功. 由 SqlSessionFactory 工厂创建 SqlSession. 每次创建 SqlSession 时,都需要由 TransactionFactory 创建 Transaction 对象,同时还需要创建 SqlSession 的执行器 Excutor,最后实例化 DefaultSqlSession,传递给 SqlSession 接口. 根据项目需求使用 SqlSession 接口中的 API 完成具体的事务操作. 如果事务执行失败,需要进行 rollback 回滚事务. 如果事务执行成功提交给数据库.关闭 SqlSession

1.3 缓存

  • 一级缓存

    一级缓存区域是根据SqlSession为单位划分的。

    每次查询会先从缓存区域找,如果找不到从数据库查询,查询到数据将数据写入缓存。

    Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象

    sqlSession执行insert、update、delete等操作commit提交后会清空缓存区域。

  • 二级缓存

    二级缓存区域是根据mapper的namespace划分的,相同namespace的mapper查询数据放在同一个区域,如果使用mapper代理方法每个mapper的namespace都不同,此时可以理解为二级缓存区域是根据mapper划分。

    每次查询会先从缓存区域找,如果找不到从数据库查询,查询到数据将数据写入缓存。

    Mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象

    sqlSession执行insert、update、delete等操作commit提交后会清空缓存区域。

    注意:二级缓存需要查询结果映射的pojo对象实现java.io.Serializable接口实现序列化和反序列化操作,注意如果存在父类、成员pojo都需要实现序列化接口。

    在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。

1.4 注意事项

#{} 和 ${} 的区别

  • #{} SQL 使用?占位符 、获取参数的内容可以使用 0(索引)、param1、arg0获取第一个参数。

    如果只有一个参数(基本数据类型或 String),mybatis 对#{}里面内容没有要求只要写内容即可. 如果时对象,则可以直接使用属性名。如果是map,则可以直接使用key

  • **${}**字符串拼接不使用?,默认找 ${内容}内容的 get/set 方法,如 果写数字,就是一个数字

Mapper特殊字符转译

如果在 xml 文件中出现 “<” , “>” ,双引号 等特殊字符时可以使用 XML 文件转义标签(XML自身的)

<![CDATA[ 内容 ]]>

事务

在 mybatis 中默认是关闭了 JDBC 的自动提交功能 ,每一个 SqlSession 默认都是不自动提交事务. 提交事务需要通过session.commit()提交事务.或者通过openSession(true);自动提交.setAutoCommit(true); 开启自动提交。

在 openSession()时 Mybatis 会创建 SqlSession 时同时创建一个 Transaction(事务对象),同时 autoCommit 都为 false ,如果出现异常,应该 session.rollback()回滚事务

增删改返回值

mybatis 底层是对 JDBC 的封装. 所以,mybatis 中<insert> <delete> <update>标签没有 resultType 属性, 认为返回值都是 int

Mysql驱动问题

MySQL驱动升级到8以后要求强制配置时区,如果不设置会出问题。

解决方式:

  1. 在url上拼接配置。serverTimezone=Asia/Shanghai
  2. 修改mysql中的配置文件mysql.ini,在mysqld项中添加default-time-zone=+8:00

二、使用示例

2.1 与spring集成

依赖

xml
<!--MyBatis依赖包-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.5</version>
</dependency>

<!--MySQL驱动包-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.6</version>
    <scope>runtime</scope>
</dependency>

配置

xml
<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> 
<!—创建一个配置文件,将连接数据库的信息存入,简化修改信息的操作  resource是配置文件的路径+名称-->
<properties resource="配置文件的路径及名称"></properties>
<!-- 配置环境参数 --> 
<settings> 
    <!-- 开启日志输出 -->
    <setting name="logImpl" value="STDOUT_LOGGING"></setting>
    <!--开启二级缓存-->
    <setting name="cacheEnabled" value="true"/>
    <!-- 开启延迟加载 --> 
    <setting name="lazyLoadingEnabled" value="true"/> 
    <!-- 关闭侵入性延迟加载 侵入性延迟加载代表,如果访问了主数据对象,关联数据自动加载。 -->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>
<!-- 类型别名 -->
<typeAliases>
    <!-- 对type值的类创建一个别名alias值,直接写alias的值即可调用type -->
    <!-- <typeAlias type=类名全路径" alias="类的别名"></typeAlias> -->
    <!-- 批量定义别名 对name包下的类起别名  别名就是类名  大小写不区分-->
    <package name="包的路径"></package>
</typeAliases>

<configuration> 
    <!-- default 引用 environment 的 id,当前所使用的环境 --> 
    <environments default="default"> 
        <!--
1.1 <transactionManager/> type 属性可取值 
	1.1.1 JDBC,事务管理使用 JDBC 原生事务管理方式 
	1.1.2 MANAGED 把事务管理转交给其他容器.原生 JDBC 事务 setAutoMapping(false);
1.2 <dataSouce/>type 属性 
	1.2.1 POOLED 使用数据库连接池
	1.2.2 UNPOOLED 不实用数据库连接池,和直接使用 JDBC 一样 
	1.2.3 JNDI :java 命名目录接口技术. -->
        <!-- 声明可以使用的环境 --> 
        <environment id="default">
            <!-- 使用原生 JDBC 事务 --> 
            <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> 
            <property name="driver" value="com.mysql.jdbc.Driver"/> 
            <!-- 使用properties时配置连接属性的方式 name和value对应配置文件中的值-->	
            <propertyname="driver" value="${键名}"></property>
            <property name="url" value="jdbc:mysql://localhost:3306/ssm"/>
            <property name="username" value="root"/>
            <property name="password" value="smallming"/> 
            </dataSource> 
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/bjsxt/mapper/FlowerMapper.xml"/>
    </mappers> 
</configuration>

mapper文件

xml
<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namesapce:理解成实现类的全路径(包名+类名) -->
<mapper namespace="a.b" > 
<mapper namespace="com.mapper.UserMapper">
	<!-- 开启当前mapper的二级缓存 -->
	<cache></cache>
</mapper>
    <!-- id:方法名 parameterType:定义参数类型 resultType:返回值类型. 如果方法返回值是 list,在 resultType 中写 List 的泛型, 因为 mybatis 对 jdbc 封装,一行一行读取数据 -->
    <select id="selAll"
resultType="com.bjsxt.pojo.Flower"> 
        select * from flower
    </select> 
</mapper>

代码

java
InputStream is = Resources.getResourceAsStream("myabtis.xml"); 
//使用工厂设计模式 
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is); 
//生产 
SqlSession SqlSession session=factory.openSession();
List<Flower> list = session.selectList("a.b.selAll"); 
for (Flower flower : list) {
    System.out.println(flower.toString()); 
}
session.close();

// 查询集合
List<Flower> list = session.selectList("a.b.selAll"); 
for (Flower flower : list) { 
    System.out.println(flower.toString());
}

// 查询单个对象
int count = session.selectOne("a.b.selById"); System.out.println(count);

// 查询map对象
Map<Object, Object> map = session.selectMap("a.b.c", "name123"); 
System.out.println(map);

2.2 springboot整合

依赖

xml
<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.0.5</version>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.1.1</version>
		</dependency>

配置

yml
spring:
  datasource:
    name: mydb
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://127.0.0.1:3306/sb_db
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver
mybatis:
  mapper-locations: classpath*:/mybatis/*Mapper.xml

扫描包路径

java
@MapperScan("包名")

配置类

java
/**
 * Mybatis支持*匹配扫描包
 *
 * @author ruoyi
 */
@Configuration
public class MyBatisConfig {
    @Autowired
    private Environment env;

    static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";

    public static String setTypeAliasesPackage(String typeAliasesPackage) {
        ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();
        MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
        List<String> allResult = new ArrayList<String>();
        try {
            for (String aliasesPackage : typeAliasesPackage.split(",")) {
                List<String> result = new ArrayList<String>();
                aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                        + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN;
                Resource[] resources = resolver.getResources(aliasesPackage);
                if (resources != null && resources.length > 0) {
                    MetadataReader metadataReader = null;
                    for (Resource resource : resources) {
                        if (resource.isReadable()) {
                            metadataReader = metadataReaderFactory.getMetadataReader(resource);
                            try {
                                result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName());
                            } catch (ClassNotFoundException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
                if (result.size() > 0) {
                    HashSet<String> hashResult = new HashSet<String>(result);
                    allResult.addAll(hashResult);
                }
            }
            if (allResult.size() > 0) {
                typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0]));
            } else {
                throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return typeAliasesPackage;
    }

    public Resource[] resolveMapperLocations(String[] mapperLocations) {
        ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
        List<Resource> resources = new ArrayList<Resource>();
        if (mapperLocations != null) {
            for (String mapperLocation : mapperLocations) {
                try {
                    Resource[] mappers = resourceResolver.getResources(mapperLocation);
                    resources.addAll(Arrays.asList(mappers));
                } catch (IOException e) {
                    // ignore
                }
            }
        }
        return resources.toArray(new Resource[resources.size()]);
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");
        String mapperLocations = env.getProperty("mybatis.mapperLocations");
        String configLocation = env.getProperty("mybatis.configLocation");
        typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
        VFS.addImplClass(SpringBootVFS.class);

        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
        sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ",")));
        sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
        return sessionFactory.getObject();
    }
}

三、基础知识

3.1 接口绑定

java
// 示例接口
public interface LogMapper { 
    List<Log> selAll(); 
    
    /**
	* mybatis 把参数转换为 map 了,其中@Param("key") 参数内 容就是 map 的 value * @param accin123 
	* @param accout3454235
	* @return 
	*/
	List<Log> selByAccInAccout(@Param("accin") String accin123,@Param("accout") String accout3454235);
}

// 示例mapper
//namespace 必须和接口全限定路径(包名+类名)一致 
//id 值必须和接口中方法名相同 
//如果接口中方法为多个参数,可以省略 parameterType
<mapper namespace="com.bjsxt.mapper.LogMapper">
    <!-- 当多参数时,不需要写 parameterType --> 
    <!-- 当Mapper类方法中没有使用@Param时时,#{}中使用 0,1,2 或 param1,param2 --> 
    <select id="selByAccInAccout" resultType="log" > 
        select * from log where accin=#{accin} and accout=#{accout} 
    </select>
</mapper>

3.2 动态sql

Mapper中逻辑判断

xml
<!-- if判断 -->
<!-- OGNL 表达式,直接写 key 或对象的属性.不需要添加任 何特字符号 --> 
<if test="accin!=null and accin!=''"> 
    and accin=#{accin} 
</if> 

<!-- where拼接 -->
<!-- 
    当编写 where 标签时,如果内容中第一个是 and 去掉第一个 and 
	如果<where>中有内容会生成 where 关键字,如果没有内容不 生成 where 关键 
-->
<where> 
    <if test="accin!=null and accin!=''">
        and accin=#{accin} 
    </if> 
</where>

<!-- chose多判断 -->
<choose> 
    <when test="accin!=null and accin!=''">
        and accin=#{accin} 
    </when> 
    <when test="accout!=null and accout!=''"> 
        and accout=#{accout} 
    </when> 
</choose>

<!-- set赋值 -->
<!-- 
	去掉最后一个逗号 
	如果<set>里面有内容生成 set 关键字,没有就不生成
-->
<set> 
    id=#{id}, 
    <if test="accIn!=null and accIn!=''"> 
        accin=#{accIn}, 
    </if> 
    <if test="accOut!=null and accOut!=''">
        accout=#{accOut},
    </if> 
</set> 

<!-- trim -->
<!-- 
    prefix 在前面添加内容 
    prefixOverrides 去掉前面内容 
    suffix 在后面添加内容 
    suffixOverrieds 去掉后面内容 
    执行顺序去掉内容后添加内容
-->
<trim prefix="set" suffixOverrides=","> 
    a=a, 
</trim>

<!-- bind -->
<!-- 给参数重新赋值 在原内容前或后添加内容 -->
<bind name="accin" value="'%'+accin+'%'"/> 

<!-- foreach -->
<!--
	collectino=”” 要遍历的集合 
    item 迭代变量, #{迭代变量名}获取内容
	open 循环后左侧添加的内容 
    close 循环后右侧添加的内容 
    separator 每次循环时,元素之间的分隔符
-->
<foreach collection="list" item="abc" open="(" close=")" separator=","> 
    #{abc} 
</foreach> 

<!-- sql -->
<!-- sql片段复用 -->
<sql id="mysql"> 
    id,accin,accout,money 
</sql>

<!-- include -->
<!-- 引用sql片段 -->
<include refid="mysql"></include> 

<!-- resultMap -->
<!-- 控制SQL查询结果与 实体类的映射关系 默认 MyBatis 使用 Auto Mapping 特性 -->
<resultMap type="teacher" id="mymap"> 
    <!-- 主键使用 id 标签配置映射关系 -->
    <id column="id" property="id1" /> 
    <!-- 其他列使用 result 标签配置映射关系 --> 
    <result column="name" property="name1"/>
</resultMap>
<!-- 使用<resultMap>标签时,<select>标签不写 resultType 属性,而是使 用 resultMap 属性引用<resultMap>标签 
useCache 禁用二级缓存  
-->
<select id="selAll" resultMap="mymap" useCache="false"> 
    select * from teacher 
</select>

<!-- resultMap中关联单个对象 -->
<resultMap type="student" id="stuMap"> 
    <result column="tid" property="tid"/> 
    <!--
		<association>装配一个对象时使用 
		property: 对象在类中的属性名 
		select:通过哪个查询查询出这个对象的信息 
		column: 把当前表的哪个列的值做为参数传递给另 一个查询 
		大前提使用 N+1 方式.时如果列名和属性名相同可以不配置,使用 Auto mapping 特性.但是 mybatis 默认只会给列专配一次 
	-->
    <association property="teacher" select="com.bjsxt.mapper.TeacherMapper.selById" column="tid"></association> 

    <!--
		column 对应sql查询的名称
		property对应实体类中属性名。
 	-->
	<association property="teacher"javaType="Teacher" > 
        <id column="tid" property="id"/> 
        <result column="tname" property="name"/> 
    </association> 
    
    <collection property="list" select="com.bjsxt.mapper.StudentMapper.selByTid" column="id"></collection> 
	 
    <collection property="list" ofType="student" > 
        <id column="sid" property="id"/> 
        <result column="sname" property="name"/> 
        <result column="age" property="age"/> 
        <result column="tid" property="tid"/> 
    </collection> 
</resultMap> 

<!-- Auto Mapping 结合别名实现多表查询 -->
<!-- 对应字段名可通过实体类的属性名.属性名方式赋值,.在 SQL 是关键字符,两侧添加反单引号 -->
<select id="selAll" resultType="student">
    select t.id `teacher.id`,t.name `teacher.name`,s.id id,s.name name,age,tid from student s LEFT JOIN teacher t on t.id=s.tid 
</select>

<!--  插入数据获取插入的id
* useGeneratedKeys:是够获取自动增长的主键值。true表示获取
* keyProperty  :指定将获取到的主键值封装到哪儿个属性里
* flushCache 刷新缓存
 -->
<insert id="add" useGeneratedKeys="true" keyProperty="id" flushCache="true">
    <!-- <selectKey resultType="java.lang.String" order="BEFORE" keyProperty="id">
		select uuid()
	</selectKey> -->
</insert>

3.3 注解使用

java
//注解可以简化 mapper.xml 文件. 但如果涉及动态 SQL 依然使用 mapper.xml 。mapper.xml 和注解可以共存. 
@Select("select * from teacher")
@Insert("insert into teacher values(default,#{name})")
@Update("update teacher set name=#{name} where id=#{id}")
@Delete("delete from teacher where id=#{0}")

@Results() //相当于<resultMap>
@Result() //相当于<id/>或<result/>
@Result(id=true) //相当与<id/>
@Many() //相当于<collection/>
@One() //相当于<association/>
@Results(value={ 
    @Result(id=true,property="id",column="id"),
    @Result(property="name",column="name"),
    @Result(property="list",column="id",many
            =@Many(select="com.bjsxt.mapper.StudentMapper.selByTid")//一对多
           )
    @Result(property = "user", column = "uid", javaType = User.class,
                    one = @One(select = "com.itheima.dao.UserDao.findById", fetchType = FetchType.LAZY))// 一对一
}) 
@Select("select * from teacher")

3.4 缓存使用

缓存可以使应用程序减少对数据库的访问交互这个耗时的过程,提升程序运行效率

缓存流程

  1. 先去缓存区中找是否存在 statement
  2. 如果有返回结果 如果没有缓存 statement 对象,去数据库获取数据
  3. 数据库返回查询结果
  4. 把查询结果放到对应的缓存区中

缓存开启

xml
<!-- sqlSession缓存 --> 
<!-- 默认开启,作用域是同一个sqlSession对象 --> 

<!-- 二级缓存 --> 
<!--
	有效范围:同一个 factory 内哪个 SqlSession 都可以获取 
	当数据频繁被使用,很少被修改时候使用二级缓存
--> 
<settings> 
<!-- 开启二级缓存的支持 --> 
<!--cacheEnabled的取值默认就为true。为true代表开启二级缓存;为false代表不开启二级缓存。--> 
<setting name="cacheEnabled" value="true"/></settings> 
@CacheNamespace(blocking=true)<!-- mybatis基于注解方式实现配置二级缓存 -->

<!-- 使用二级缓存 -->
<!--
	在 mapper.xml 中添加
	如果不写 readOnly=”true”需要把实体类序列化
-->
<cache readOnly="true"></cache>
<!-- 当 SqlSession 对象 close()时或 commit()时会把 SqlSession 缓存 的数据刷(flush)到 SqlSessionFactory 缓存区中 -->

3.5 分页插件

原理:通过拦截Executor 执行器的执行语句,在其后拼接分页信息。

依赖

xml
<!-- 分页插件 --> 
<dependency> 
    <groupId>com.github.pagehelper</groupId> 
    <artifactId>pagehelper</artifactId> 
    <version>4.1.6</version> 
</dependency>

<dependency> 
    <groupId>com.github.pagehelper</groupId> 
    <artifactId>pagehelper‐spring‐boot‐starter</artifactId>
    <version>1.2.4</version> 
 </dependency>

配置说明

xml
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<!-- 注意其他配置 -->
	<property name="plugins">
		<array>
			<bean class="com.github.pagehelper.PageInterceptor">
				<property name="properties">
					<!--使用下面的方式配置参数,一行配置一个 -->
					<value>
						params=value1
					</value>
				</property>
			</bean>
		</array>
	</property>
 </bean>

参数介绍:同下配置的property中的name和value

  1. helperDialect :分页插件会自动检测当前的数据库链接,自动选择合适的分页方式。 你可以配置helperDialect 属性来指定分页插件使用哪种方言。配置时,可以使用下面的缩写值:oracle , mysql , mariadb , sqlite , hsqldb , postgresql , db2 , sqlserver , informix , h2 , sqlserver2012 , derby 特别注意:使用 SqlServer2012 数据库时,需要手动指定为 sqlserver2012 ,否则会使用 SqlServer2005 的方式进行分页。你也可以实现 AbstractHelperDialect ,然后配置该属性为实现类的全限定名称即可使用自定义的实现方法。
  2. offsetAsPageNum :默认值为 false ,该参数对使用 RowBounds 作为分页参数时有效。 当该参数设置为true 时,会将 RowBounds 中的 offset 参数当成 pageNum 使用,可以用页码和页面大小两个参数进行分页。
  3. rowBoundsWithCount :默认值为false ,该参数对使用 RowBounds 作为分页参数时有效。 当该参数设置为true 时,使用 RowBounds 分页会进行 count 查询。
  4. pageSizeZero :默认值为 false ,当该参数设置为 true 时,如果 pageSize=0 或者 RowBounds.limit =0 就会查询出全部的结果(相当于没有执行分页查询,但是返回结果仍然是 Page 类型)。
  5. reasonable :分页合理化参数,默认值为false 。当该参数设置为 true 时, pageNum<=0 时会查询第一页, pageNum>pages (超过总数时),会查询最后一页。默认false 时,直接根据参数进行查询。
  6. params :为了支持startPage(Object params) 方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值, 可以配置 pageNum,pageSize,count,pageSizeZero,reasonable ,不配置映射的用默认值, 默认值为pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero。
  7. supportMethodsArguments :支持通过 Mapper 接口参数来传递分页参数,默认值false ,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页。 使用方法可以参考测试代码中的 com.github.pagehelper.test.basic 包下的 ArgumentsMapTest 和ArgumentsObjTest 。
  8. autoRuntimeDialect :默认值为 false 。设置为 true 时,允许在运行时根据多数据源自动识别对应方言的分页 (不支持自动选择sqlserver2012 ,只能使用sqlserver )。
  9. closeConn :默认值为 true 。当使用运行时动态数据源或没有设置 helperDialect 属性自动获取数据库类型时,会自动获取一个数据库连接, 通过该属性来设置是否关闭获取的这个连接,默认true 关闭,设置为false 后,不会关闭获取的连接,这个参数的设置要根据自己选择的数据源来决定。

mybatis配置

xml
<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration> 
    <plugins> 
        <plugin interceptor="com.github.pagehelper.PageHelper"> 
            <!-- 告诉分页插件是哪个数据库 --> 
            <property name="dialect" value="mysql"/> </plugin> 
    </plugins> 
 </configuration>

spring配置

xml
<!-- SqlSessionFactory --> 
   <bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean"> 
       <property name="dataSource" ref="dataSource"></property> 
       <property name="typeAliasesPackage" value="com.ego.pojo"></property>
     <property name="configLocation" value="classpath:mybatis.xml"></property> 
</bean>

代码实现

java
List<Country> list = sqlSession.selectList("x.y.selectIf", null, new RowBounds(1, 10));
//这种情况下也会进行物理分页查询
List<Country> selectAll(RowBounds rowBounds);
 //由于默认情况下的 RowBounds 无法获取查询总数,分页插件提供了一个继承自 RowBounds 的PageRowBounds ,这个对象中增加了 total 属性,执行分页查询后,可以从该属性得到查询总数。

PageHelper.startPage(page, rows);
//查询全部 
List<TbItem> list = tbItemMapper.selectByExample(new TbItemExample()); 
//分页代码 
//设置分页条件 
PageInfo<TbItem> pi = new PageInfo<>(list);

3.6 整合ehcache缓存

通过实现Cache接口可以实现mybatis缓存数据通过其它缓存数据库整合,mybatis的特长是sql操作,缓存数据的管理不是mybatis的特长,为了提高缓存的性能将mybatis和第三方的缓存数据库整合,比如ehcache、memcache、redis等。

依赖

xml
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.0.2</version>
</dependency>

ehcache配置文件

xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
	<diskStore path="F:\develop\ehcache" />
	<defaultCache 
		maxElementsInMemory="1000" 
		maxElementsOnDisk="10000000"
		eternal="false" 
		overflowToDisk="false" 
		timeToIdleSeconds="120"
		timeToLiveSeconds="120" 
		diskExpiryThreadIntervalSeconds="120"
		memoryStoreEvictionPolicy="LRU">
	</defaultCache>
</ehcache>

属性说明:

  • diskStore:指定数据在磁盘中的存储位置。
  • defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略

以下属性是必须的:

  • maxElementsInMemory - 在内存中缓存的element的最大数目
  • maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大
  • eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
  • overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上

以下属性是可选的:

  • timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
  • timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
  • diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.
  • diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
  • diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作
  • memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)

开启缓存

修改mapper.xml

xml
<cache type="org.mybatis.caches.ehcache.EhcacheCache" > 
    <property name="timeToIdleSeconds" value="3600"/>
    <property name="timeToLiveSeconds" value="3600"/>
    <!-- 同ehcache参数maxElementsInMemory -->
    <property name="maxEntriesLocalHeap" value="1000"/>
    <!-- 同ehcache参数maxElementsOnDisk -->
    <property name="maxEntriesLocalDisk" value="10000000"/>
    <property name="memoryStoreEvictionPolicy" value="LRU"/>
</cache>

四、附录

4.1 mybatis相关文档

【MyBatis】自定义MyBatis的实现_骑着蜗牛ひ追导弹'的博客-CSDN博客

mybatis – MyBatis 3 | 简介

4.2 mybatis-settings配置

Setting(设置)Description(描述)Valid Values(验证值组)Default(默认值)
cacheEnabled在全局范围内启用或禁用缓存配置任何映射器在此配置下。true | falseTRUE
lazyLoadingEnabled在全局范围内启用或禁用延迟加载。禁用时,所有协会将热加载。true | falseTRUE
aggressiveLazyLoading启用时,有延迟加载属性的对象将被完全加载后调用懒惰的任何属性。否则,每一个属性是按需加载。true | falseTRUE
multipleResultSetsEnabled允许或不允许从一个单独的语句(需要兼容的驱动程序)要返回多个结果集。true | falseTRUE
useColumnLabel使用列标签,而不是列名。在这方面,不同的驱动有不同的行为。参考驱动文档或测试两种方法来决定你的驱动程序的行为如何。true | falseTRUE
useGeneratedKeys允许JDBC支持生成的密钥。兼容的驱动程序是必需的。此设置强制生成的键被使用,如果设置为true,一些驱动会不兼容性,但仍然可以工作。true | falseFALSE
autoMappingBehavior指定MyBatis的应如何自动映射列到字段/属性。NONE自动映射。 PARTIAL只会自动映射结果没有嵌套结果映射定义里面。 FULL会自动映射的结果映射任何复杂的(包含嵌套或其他)。NONE, PARTIAL, FULLPARTIAL
defaultExecutorType配置默认执行人。SIMPLE执行人确实没有什么特别的。 REUSE执行器重用准备好的语句。 BATCH执行器重用语句和批处理更新。SIMPLE REUSE BATCHSIMPLE
defaultStatementTimeout设置驱动程序等待一个数据库响应的秒数。Any positive integerNot Set (null)
safeRowBoundsEnabled允许使用嵌套的语句RowBounds。true | falseFALSE
mapUnderscoreToCamelCase从经典的数据库列名A_COLUMN启用自动映射到骆驼标识的经典的Java属性名aColumn。true | falseFALSE
localCacheScopeMyBatis的使用本地缓存,以防止循环引用,并加快反复嵌套查询。默认情况下(SESSION)会话期间执行的所有查询缓存。如果localCacheScope=STATMENT本地会话将被用于语句的执行,只是没有将数据共享之间的两个不同的调用相同的SqlSession。SESSION | STATEMENTSESSION
dbcTypeForNull指定为空值时,没有特定的JDBC类型的参数的JDBC类型。有些驱动需要指定列的JDBC类型,但其他像NULL,VARCHAR或OTHER的工作与通用值。JdbcType enumeration. Most common are: NULL, VARCHAR and OTHEROTHER
lazyLoadTriggerMethods指定触发延迟加载的对象的方法。A method name list separated by commasequals,clone,hashCode,toString
defaultScriptingLanguage指定所使用的语言默认为动态SQL生成。A type alias or fully qualified class name.org.apache.ibatis.scripting.xmltags.XMLDynamicLanguageDriver
callSettersOnNulls指定如果setter方法或地图的put方法时,将调用检索到的值是null。它是有用的,当你依靠Map.keySet()或null初始化。注意原语(如整型,布尔等)不会被设置为null。true | falseFALSE
logPrefix指定的前缀字串,MyBatis将会增加记录器的名称。Any StringNot set
logImpl指定MyBatis的日志实现使用。如果此设置是不存在的记录的实施将自动查找。SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGINGNot set
proxyFactory指定代理工具,MyBatis将会使用创建懒加载能力的对象。CGLIB | JAVASSIST

4.3 mybatis默认支持别名

别名映射的类型
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal